#include "MySqlSelectParser.h"
#include "../../../parser/SQLSelectListCache.h"
#include "../../../parser/Lexer.h"
#include "../../../../utils/instanceof.h"
#include "../../../../utils/FnvHash.h"
#include "MySqlExprParser.h"
#include "../../../ast/statement/SQLSelectQuery.h"
#include "../../../ast/SQLCommentHint.h"
#include "../../../ast/statement/SQLUnionQueryTableSource.h"
#include "../ast/statement/MySqlSelectQueryBlock.h"
#include "../ast/statement/MySqlUpdateStatement.h"
#include "../../../../utils/log.h"
#include "../../../ast/statement/SQLSelectItem.h"

MySqlSelectParser::MySqlSelectParser(SQLExprParser_ptr exprParser)
    : SQLSelectParser(exprParser)
{
  returningFlag = BOOL::FALSE;
}

MySqlSelectParser::MySqlSelectParser(SQLExprParser_ptr exprParser, SQLSelectListCache_ptr selectListCache)
    : SQLSelectParser(exprParser, selectListCache)
{
  LOG_INFO << "in MySqlSelectParser";
  returningFlag = BOOL::FALSE;
}

MySqlSelectParser::MySqlSelectParser(string_ptr sql)
    : MySqlSelectParser(MySqlExprParser_ptr(new MySqlExprParser(sql)))
{
  returningFlag = BOOL::FALSE;
}

void MySqlSelectParser::parseFrom(SQLSelectQueryBlock_ptr queryBlock)
{
  LOG_INFO << "token name:" << lexer->token()->getName()->c_str();

  if (lexer->token()->name->c_str() == Token::XEOF.name->c_str() ||
      lexer->token()->name->c_str() == Token::SEMI.name->c_str() ||
      lexer->token()->name->c_str() == Token::ORDER.name->c_str() ||
      lexer->token()->name->c_str() == Token::RPAREN.name->c_str() ||
      lexer->token()->name->c_str() == Token::UNION.name->c_str())
  {
    return;
  }

  if (lexer->token()->name->c_str() != Token::FROM.name->c_str())
  {
    for (SQLSelectItem_ptr item : *queryBlock->getSelectList())
    {
      SQLExpr_ptr expr = item->getExpr();
      if (instanceof <SQLExpr, SQLAggregateExpr>(expr))
      {
        throw new ParserException(make_string_ptr("syntax error, expect " + *Token::FROM.getName() + ", actual " + *(lexer->token()->getName()) + ", " + encode_util::UnicodeToUTF8(lexer->info())));
      }
    }
    return;
  }

  lexer->nextTokenIdent();

  while (lexer->token()->name->c_str() == Token::HINT.name->c_str())
  {
    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == Token::TABLE.name->c_str())
  {
    // HiveCreateTableParser* createTableParser = new HiveCreateTableParser(lexer);
    // HiveCreateTableStatement* stmt = (HiveCreateTableStatement)createTableParser.parseCreateTable(BOOL::FALSE);
    // SQLAdhocTableSource* tableSource = new SQLAdhocTableSource(stmt);
    // queryBlock->setFrom(parseTableSourceRest(tableSource));
    // return;
  }

  if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
  { // taobao returning to urgly syntax
    // updateStmt = this->parseUpdateStatment();
    // std::list<SQLExpr_ptr > returnning = updateStmt->getReturning();
    // for (SQLSelectItem_ptr item : queryBlock->getSelectList())
    // {
    //   SQLExpr_ptr itemExpr = item->getExpr();
    //   itemExpr->setParent(updateStmt);
    //   returnning.push_back(itemExpr);
    // }
    // returningFlag = BOOL::TRUE;
    // return;
  }

  SQLTableSource_ptr from = parseTableSource(queryBlock);
  queryBlock->setFrom(from);
}

class QueryHintHandler : public Lexer_CommentHandler
{
public:
  MySqlSelectQueryBlock_ptr queryBlock;
  Lexer_ptr lexer;

  QueryHintHandler(MySqlSelectQueryBlock_ptr queryBlock, Lexer_ptr lexer)
  {
    this->queryBlock = queryBlock;
    this->lexer = lexer;
  }

  BOOL_ptr handle(Token_ptr lastToken, string_ptr comment)
  {
    if (lexer->isEnabled(SQLParserFeature_ptr(new SQLParserFeature(&SQLParserFeature::TDDLHint))) &&
        (StringUtils::startWith(comment, make_string_ptr("+ TDDL")) ||
         StringUtils::startWith(comment, make_string_ptr("+TDDL")) ||
         StringUtils::startWith(comment, make_string_ptr("!TDDL")) ||
         StringUtils::startWith(comment, make_string_ptr("TDDL"))))
    {
      // SQLCommentHint* hint = new TDDLHint(comment);

      if (lexer->getCommentCount() > 0)
      {
        // hint.addBeforeComment(lexer->getComments());
      }

      // queryBlock->getHints().add(hint);

      lexer->nextToken();
    }
    return BOOL::FALSE;
  }
};
typedef std::shared_ptr<QueryHintHandler> QueryHintHandler_ptr;

SQLSelectQuery_ptr MySqlSelectParser::query(SQLObject_ptr parent, BOOL_ptr acceptUnion)
{
  LOG_INFO << "token name:" << lexer->token()->getName()->c_str();
  SQLCommentHint_list_ptr hints = std::make_shared<std::list<SQLCommentHint_ptr>>();
  if (lexer->token()->name->c_str() == Token::HINT.name->c_str())
  {
    LOG_INFO << "goto parseHints";
    hints = this->exprParser->parseHints();
  }

  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();
    LOG_INFO << "token name:" << lexer->token()->getName()->c_str();
    SQLSelectQuery_ptr select = SQLSelectParser::query();
    select->setParenthesized(BOOL::TRUE);
    accept(Token::RPAREN);

    return queryRest(select, acceptUnion);
  }

  if (lexer->token()->name->c_str() == Token::VALUES.name->c_str())
  {
    return valuesQuery(acceptUnion);
  }

  MySqlSelectQueryBlock_ptr queryBlock = MySqlSelectQueryBlock_ptr(new MySqlSelectQueryBlock());
  queryBlock->setParent(parent);

  this->lexer->setCommentHandler(QueryHintHandler_ptr(new QueryHintHandler(queryBlock, this->lexer)));

  if (lexer->hasComment() && lexer->isKeepComments())
  {
    queryBlock->addBeforeComment(lexer->readAndResetComments());
  }

  if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
  {
    if (selectListCache != nullptr)
    {
      LOG_INFO << "goto match";
      selectListCache->match(lexer, queryBlock);
    }
  }

  if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
  {
    LOG_INFO << "goto nextTokenValue";
    lexer->nextTokenValue();

    for (;;)
    {
      LOG_INFO << "token name:" << lexer->token()->name->c_str();
      if (lexer->token()->name->c_str() == Token::HINT.name->c_str())
      {
        this->exprParser->parseHints(queryBlock->getHints());
      }
      else
      {
        break;
      }
    }

    while (BOOL::TRUE)
    {
      Token_ptr token = lexer->token();
      LOG_INFO << "token name:" << lexer->token()->getName()->c_str();
      if (token->name->c_str() == (Token::DISTINCT.name->c_str()))
      {
        queryBlock->setDistionOption(SQLSetQuantifier::DISTINCT);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::DISTINCTROW))
      {
        queryBlock->setDistionOption(SQLSetQuantifier::DISTINCTROW);
        lexer->nextToken();
      }
      else if (token->name->c_str() == (Token::ALL.name->c_str()))
      {
        queryBlock->setDistionOption(SQLSetQuantifier::ALL);
        lexer->nextToken();
      }
      else if (token->name->c_str() == (Token::UNIQUE.name->c_str()))
      {
        queryBlock->setDistionOption(SQLSetQuantifier::UNIQUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::HIGH_PRIORITY))
      {
        queryBlock->setHignPriority(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::STRAIGHT_JOIN))
      {
        queryBlock->setStraightJoin(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_SMALL_RESULT))
      {
        queryBlock->setSmallResult(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_BIG_RESULT))
      {
        queryBlock->setBigResult(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_BUFFER_RESULT))
      {
        queryBlock->setBufferResult(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_CACHE))
      {
        queryBlock->setCache(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_NO_CACHE))
      {
        queryBlock->setCache(BOOL::FALSE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::SQL_CALC_FOUND_ROWS))
      {
        queryBlock->setCalcFoundRows(BOOL::TRUE);
        lexer->nextToken();
      }
      else if (lexer->identifierEquals(FnvHash::Constants::TOP))
      {
        Lexer_SavePoint_ptr mark = lexer->mark();

        lexer->nextToken();
        if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str())
        {
          SQLLimit_ptr limit = SQLLimit_ptr(new SQLLimit(lexer->integerValue()));
          queryBlock->setLimit(limit);
          lexer->nextToken();
        }
        else if (lexer->token()->name->c_str() == Token::DOT.name->c_str())
        {
          lexer->reset(mark);
          break;
        }
      }
      else
      {
        break;
      }
    }

    parseSelectList(queryBlock);

    if (lexer->identifierEquals(FnvHash::Constants::FORCE))
    {
      lexer->nextToken();
      accept(Token::PARTITION);
      SQLName_ptr partition = this->exprParser->name();
      queryBlock->setForcePartition(partition);
    }

    parseInto(queryBlock);
  }

  parseFrom(queryBlock);

  parseWhere(queryBlock);

  SQLSelectParser::parseHierachical(queryBlock);

  if (lexer->token()->name->c_str() == Token::GROUP.name->c_str() ||
      lexer->token()->name->c_str() == Token::HAVING.name->c_str())
  {
    parseGroupBy(queryBlock);
  }

  if (lexer->identifierEquals(FnvHash::Constants::WINDOW))
  {
    parseWindow(queryBlock);
  }

  if (lexer->token()->name->c_str() == Token::ORDER.name->c_str())
  {
    queryBlock->setOrderBy(this->exprParser->parseOrderBy());
  }

  if (lexer->token()->name->c_str() == Token::LIMIT.name->c_str())
  {
    queryBlock->setLimit(this->exprParser->parseLimit());
  }

  if (lexer->token()->name->c_str() == Token::FETCH.name->c_str())
  {
    Lexer_SavePoint_ptr mark = lexer->mark();
    lexer->nextToken();
    if (lexer->identifierEquals(FnvHash::Constants::NEXT))
    {
      lexer->nextToken();
      SQLExpr_ptr rows = this->exprParser->primary();
      queryBlock->setLimit(SQLLimit_ptr(new SQLLimit(rows)));
      acceptIdentifier("ROWS");
      acceptIdentifier("ONLY");
    }
    else
    {
      lexer->reset(mark);
    }
  }

  if (lexer->token()->name->c_str() == Token::PROCEDURE.name->c_str())
  {
    lexer->nextToken();
    throw new ParserException(make_string_ptr("TODO. " + encode_util::UnicodeToUTF8(lexer->info())));
  }

  if (lexer->token()->name->c_str() == Token::INTO.name->c_str())
  {
    parseInto(queryBlock);
  }

  if (lexer->token()->name->c_str() == Token::FOR.name->c_str())
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
    {
      lexer->nextToken();
      queryBlock->setForUpdate(BOOL::TRUE);

      if (lexer->identifierEquals(FnvHash::Constants::NO_WAIT) || lexer->identifierEquals(FnvHash::Constants::NOWAIT))
      {
        lexer->nextToken();
        queryBlock->setNoWait(BOOL::TRUE);
      }
      else if (lexer->identifierEquals(FnvHash::Constants::WAIT))
      {
        lexer->nextToken();
        SQLExpr_ptr waitTime = this->exprParser->primary();
        queryBlock->setWaitTime(waitTime);
      }

      if (lexer->identifierEquals(FnvHash::Constants::SKIP))
      {
        lexer->nextToken();
        acceptIdentifier("LOCKED");
        queryBlock->setSkipLocked(BOOL::TRUE);
      }
    }
    else
    {
      acceptIdentifier("SHARE");
      queryBlock->setForShare(BOOL::TRUE);
    }
  }

  if (lexer->token()->name->c_str() == Token::LOCK.name->c_str())
  {
    lexer->nextToken();
    accept(Token::IN);
    acceptIdentifier("SHARE");
    acceptIdentifier("MODE");
    queryBlock->setLockInShareMode(BOOL::TRUE);
  }

  if (!hints->empty())
  {
    queryBlock->setHints(hints);
  }

  return queryRest(std::shared_ptr<MySqlSelectQueryBlock>(queryBlock), acceptUnion);
}

SQLTableSource_ptr MySqlSelectParser::parseTableSource()
{
  return parseTableSource(NULL);
}

SQLTableSource_ptr MySqlSelectParser::parseTableSource(SQLObject_ptr parent)
{
  if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
  {
    lexer->nextToken();

    SQLCommentHint_list_ptr hints;
    if (lexer->token()->name->c_str() == Token::HINT.name->c_str())
    {
      // hints = new ArrayList();
      hints = std::make_shared<std::list<SQLCommentHint_ptr>>();
      this->exprParser->parseHints(hints);
    }

    SQLTableSource_ptr tableSource;
    if (lexer->token()->name->c_str() == Token::SELECT.name->c_str() || lexer->token()->name->c_str() == Token::WITH.name->c_str())
    {
      SQLSelect_ptr select_ = select();

      accept(Token::RPAREN);

      SQLSelectQueryBlock_ptr innerQuery = select_->getQueryBlock();

      BOOL_ptr noOrderByAndLimit =
          (innerQuery->getOrderBy() == NULL && (std::dynamic_pointer_cast<SQLSelectQueryBlock>(select_->getQuery()))->getLimit() == NULL) ? BOOL::TRUE : BOOL::FALSE;

      if (lexer->token()->name->c_str() == Token::LIMIT.name->c_str())
      {
        SQLLimit_ptr limit = this->exprParser->parseLimit();
        if (parent != NULL && instanceof <SQLObject, SQLSelectQueryBlock>(parent))
        {
          (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->setLimit(limit);
        }
        if (parent == NULL && noOrderByAndLimit)
        {
          innerQuery->setLimit(limit);
        }
      }
      else if (lexer->token()->name->c_str() == Token::ORDER.name->c_str())
      {
        SQLOrderBy_ptr orderBy = this->exprParser->parseOrderBy();
        if (parent != NULL && instanceof <SQLObject, SQLSelectQueryBlock>(parent))
        {
          (std::dynamic_pointer_cast<SQLSelectQueryBlock>(parent))->setOrderBy(orderBy);
        }
        if (parent == NULL && noOrderByAndLimit)
        {
          innerQuery->setOrderBy(orderBy);
        }
      }

      SQLSelectQuery_ptr query = queryRest(select_->getQuery(), BOOL::FALSE);
      if (instanceof <SQLSelectQuery, SQLUnionQuery>(query) && select_->getWithSubQuery() == NULL)
      {
        select_->getQuery()->setParenthesized(BOOL::TRUE);
        tableSource = SQLTableSource_ptr(new SQLUnionQueryTableSource(std::dynamic_pointer_cast<SQLUnionQuery>(query)));
      }
      else
      {
        tableSource = SQLTableSource_ptr(new SQLSubqueryTableSource(select_));
      }

      if (!hints->empty())
      {
        // tableSource->getHints().addAll(hints);
        for (auto ele : *hints)
        {
          tableSource->getHints()->push_back(ele);
        }
      }
    }
    else if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      tableSource = parseTableSource();
      if (lexer->token()->name->c_str() != Token::RPAREN.name->c_str() && instanceof <SQLTableSource, SQLSubqueryTableSource>(tableSource))
      {
        std::shared_ptr<SQLSubqueryTableSource> sqlSubqueryTableSource = std::dynamic_pointer_cast<SQLSubqueryTableSource>(tableSource);
        SQLSelect_ptr select = sqlSubqueryTableSource->getSelect();

        SQLSelectQuery_ptr query = queryRest(select->getQuery(), BOOL::TRUE);
        if (instanceof <SQLSelectQuery, SQLUnionQuery>(query) && select->getWithSubQuery() == NULL)
        {
          select->getQuery()->setParenthesized(BOOL::TRUE);
          tableSource = SQLTableSource_ptr(new SQLUnionQueryTableSource(std::dynamic_pointer_cast<SQLUnionQuery>(query)));
        }
        else
        {
          tableSource = SQLTableSource_ptr(new SQLSubqueryTableSource(select));
        }

        if (!hints->empty())
        {
          // tableSource->getHints().addAll(hints);
          for (auto ele : *hints)
          {
            tableSource->getHints()->push_back(ele);
          }
        }
      }
      else if (lexer->token()->name->c_str() != Token::RPAREN.name->c_str() && instanceof <SQLTableSource, SQLUnionQueryTableSource>(tableSource))
      {
        std::shared_ptr<SQLUnionQueryTableSource> unionQueryTableSource = std::dynamic_pointer_cast<SQLUnionQueryTableSource>(tableSource);
        SQLUnionQuery_ptr unionQuery = unionQueryTableSource->getUnion();

        SQLSelectQuery_ptr query = queryRest(unionQuery, BOOL::TRUE);
        if (instanceof <SQLSelectQuery, SQLUnionQuery>(query))
        {
          unionQuery->setParenthesized(BOOL::TRUE);
          tableSource = SQLUnionQueryTableSource_ptr(new SQLUnionQueryTableSource(std::dynamic_pointer_cast<SQLUnionQuery>(query)));
        }
        else
        {
          tableSource = std::shared_ptr<SQLSubqueryTableSource>(new SQLSubqueryTableSource(unionQuery));
        }

        if (!hints->empty())
        {
          // tableSource.getHints().addAll(hints);
          for (auto ele : *hints)
          {
            tableSource->getHints()->push_back(ele);
          }
        }
      }
      accept(Token::RPAREN);
    }
    else
    {
      tableSource = parseTableSource();
      accept(Token::RPAREN);
      // if (lexer->token()->name->c_str() == Token::AS && instanceof<SQLTableSource, SQLValuesTableSource>(tableSource ) )
      // {
      //   lexer->nextToken();
      //   string_ptr  alias = lexer->stringVal();
      //   lexer->nextToken();
      //   tableSource->setAlias(alias);
      //   accept(Token::LPAREN);
      //   SQLValuesTableSource* values = (SQLValuesTableSource)tableSource;
      //   this->exprParser->names(values.getColumns(), tableSource);
      //   accept(Token::RPAREN);
      // }
    }

    return parseTableSourceRest(tableSource);
  }
  else if (lexer->token()->name->c_str() == Token::LBRACE.name->c_str())
  {
    accept(Token::LBRACE);
    acceptIdentifier("OJ");

    SQLTableSource_ptr tableSrc = parseTableSource();

    accept(Token::RBRACE);

    tableSrc = parseTableSourceRest(tableSrc);

    if (lexer->hasComment() && lexer->isKeepComments())
    {
      string_list_ptr tmp = std::make_shared<std::list<string_ptr>>();
      for (auto it : *lexer->readAndResetComments())
      {
        tmp->push_back(make_string_ptr(encode_util::UnicodeToUTF8(*it)));
      }
      tableSrc->addAfterComment(tmp);
    }

    return tableSrc;
  }

  if (lexer->token()->name->c_str() == Token::VALUES.name->c_str())
  {
    // return parseValues();
  }

  if (lexer->token()->name->c_str() == Token::UPDATE.name->c_str())
  {
    // SQLTableSource_ptr tableSource = new MySqlUpdateTableSource(parseUpdateStatment());
    // return parseTableSourceRest(tableSource);
  }

  if (lexer->token()->name->c_str() == Token::SELECT.name->c_str())
  {
    throw new ParserException(make_string_ptr("TODO. " + encode_util::UnicodeToUTF8(lexer->info())));
  }

  if (lexer->identifierEquals(FnvHash::Constants::UNNEST))
  {
    Lexer_SavePoint_ptr mark = lexer->mark();
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();
      // SQLUnnestTableSource* unnest = new SQLUnnestTableSource();
      // this->exprParser->exprList(unnest.getItems(), unnest);
      accept(Token::RPAREN);

      if (lexer->token()->name->c_str() == Token::WITH.name->c_str())
      {
        lexer->nextToken();
        acceptIdentifier("ORDINALITY");
        // unnest.setOrdinality(BOOL::TRUE);
      }

      string_ptr alias = this->tableAlias();
      // unnest.setAlias(alias);

      if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
      {
        lexer->nextToken();
        // this->exprParser->names(unnest.getColumns(), unnest);
        accept(Token::RPAREN);
      }

      // SQLTableSource_ptr tableSrc = parseTableSourceRest(unnest);
      // return tableSrc;
    }
    else
    {
      lexer->reset(mark);
    }
  }

  SQLExprTableSource_ptr tableReference = SQLExprTableSource_ptr(new SQLExprTableSource());

  parseTableSourceQueryTableExpr(tableReference);

  SQLTableSource_ptr tableSrc = parseTableSourceRest(tableReference);

  if (lexer->hasComment() && lexer->isKeepComments())
  {
    string_list_ptr tmp = std::make_shared<std::list<string_ptr>>();
    for (auto it : *lexer->readAndResetComments())
    {
      tmp->push_back(make_string_ptr(encode_util::UnicodeToUTF8(*it)));
    }
    tableSrc->addAfterComment(tmp);
  }

  return tableSrc;
}

MySqlUpdateStatement_ptr MySqlSelectParser::parseUpdateStatment()
{
  MySqlUpdateStatement_ptr update = MySqlUpdateStatement_ptr(new MySqlUpdateStatement());

  lexer->nextToken();

  if (lexer->identifierEquals(FnvHash::Constants::LOW_PRIORITY))
  {
    lexer->nextToken();
    update->setLowPriority(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::IGNORE))
  {
    lexer->nextToken();
    update->setIgnore(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::COMMIT_ON_SUCCESS))
  {
    lexer->nextToken();
    update->setCommitOnSuccess(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::ROLLBACK_ON_FAIL))
  {
    lexer->nextToken();
    update->setRollBackOnFail(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::QUEUE_ON_PK))
  {
    lexer->nextToken();
    update->setQueryOnPk(BOOL::TRUE);
  }

  if (lexer->identifierEquals(FnvHash::Constants::TARGET_AFFECT_ROW))
  {
    lexer->nextToken();
    SQLExpr_ptr targetAffectRow = this->exprParser->expr();
    update->setTargetAffectRow(targetAffectRow);
  }

  if (lexer->identifierEquals(FnvHash::Constants::FORCE))
  {
    lexer->nextToken();

    if (lexer->token()->name->c_str() == Token::ALL.name->c_str())
    {
      lexer->nextToken();
      acceptIdentifier("PARTITIONS");
      update->setForceAllPartitions(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::PARTITIONS))
    {
      lexer->nextToken();
      update->setForceAllPartitions(BOOL::TRUE);
    }
    else if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
    {
      lexer->nextToken();
      SQLName_ptr partition = this->exprParser->name();
      update->setForcePartition(partition);
    }
    else
    {
      throw new ParserException(make_string_ptr("TODO. " + encode_util::UnicodeToUTF8(lexer->info())));
    }
  }

  while (lexer->token()->name->c_str() == Token::HINT.name->c_str())
  {
    this->exprParser->parseHints(update->getHints());
  }

  SQLSelectParser_ptr selectParser = this->exprParser->createSelectParser();
  SQLTableSource_ptr updateTableSource = selectParser->parseTableSource();
  update->setTableSource(updateTableSource);

  accept(Token::SET);

  for (;;)
  {
    SQLUpdateSetItem_ptr item = this->exprParser->parseUpdateSetItem();
    update->addItem(item);

    if (lexer->token()->name->c_str() != Token::COMMA.name->c_str())
    {
      break;
    }

    lexer->nextToken();
  }

  if (lexer->token()->name->c_str() == (Token::WHERE.name->c_str()))
  {
    lexer->nextToken();
    update->setWhere(this->exprParser->expr());
  }

  update->setOrderBy(this->exprParser->parseOrderBy());
  update->setLimit(this->exprParser->parseLimit());

  return update;
}

void MySqlSelectParser::parseInto(SQLSelectQueryBlock_ptr queryBlock)
{
  if (lexer->token()->name->c_str() != Token::INTO.name->c_str())
  {
    return;
  }

  lexer->nextToken();

  if (lexer->identifierEquals(FnvHash::Constants::OUTFILE))
  {
    lexer->nextToken();

    // MySqlOutFileExpr* outFile = new MySqlOutFileExpr();
    // outFile.setFile(expr());

    // queryBlock->setInto(outFile);

    if (lexer->identifierEquals(FnvHash::Constants::FIELDS) || lexer->identifierEquals(FnvHash::Constants::COLUMNS))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(FnvHash::Constants::TERMINATED))
      {
        lexer->nextToken();
        accept(Token::BY);
      }
      // outFile.setColumnsTerminatedBy(expr());

      if (lexer->identifierEquals(FnvHash::Constants::OPTIONALLY))
      {
        lexer->nextToken();
        // outFile.setColumnsEnclosedOptionally(BOOL::TRUE);
      }

      if (lexer->identifierEquals(FnvHash::Constants::ENCLOSED))
      {
        lexer->nextToken();
        accept(Token::BY);
        // outFile.setColumnsEnclosedBy((SQLLiteralExpr)expr());
      }

      if (lexer->identifierEquals(FnvHash::Constants::ESCAPED))
      {
        lexer->nextToken();
        accept(Token::BY);
        // outFile.setColumnsEscaped((SQLLiteralExpr)expr());
      }
    }

    if (lexer->identifierEquals(FnvHash::Constants::LINES))
    {
      lexer->nextToken();

      if (lexer->identifierEquals(FnvHash::Constants::STARTING))
      {
        lexer->nextToken();
        accept(Token::BY);
        // outFile.setLinesStartingBy((SQLLiteralExpr)expr());
      }
      else
      {
        if (lexer->identifierEquals(FnvHash::Constants::TERMINATED))
        {
          lexer->nextToken();
        }
        accept(Token::BY);
        // outFile.setLinesTerminatedBy((SQLLiteralExpr)expr());
      }
    }
  }
  else
  {
    SQLExpr_ptr intoExpr = this->exprParser->name();
    if (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
    {
      SQLListExpr_ptr list = SQLListExpr_ptr(new SQLListExpr());
      list->addItem(intoExpr);

      while (lexer->token()->name->c_str() == Token::COMMA.name->c_str())
      {
        lexer->nextToken();
        SQLName_ptr name = this->exprParser->name();
        list->addItem(name);
      }

      intoExpr = list;
    }
    queryBlock->setInto(intoExpr);
  }
}

SQLTableSource_ptr MySqlSelectParser::primaryTableSourceRest(SQLTableSource_ptr tableSource)
{
  if (lexer->token()->name->c_str() == Token::USE.name->c_str())
  {
    lexer->nextToken();
    // MySqlUseIndexHint hint = new MySqlUseIndexHint();
    // parseIndexHint(hint);
    // tableSource.getHints().add(hint);
  }

  if (lexer->identifierEquals(FnvHash::Constants::IGNORE))
  {
    lexer->nextToken();
    // MySqlIgnoreIndexHint hint = new MySqlIgnoreIndexHint();
    // parseIndexHint(hint);
    // tableSource.getHints().add(hint);
  }

  if (lexer->identifierEquals(FnvHash::Constants::FORCE))
  {
    lexer->nextToken();
    // MySqlForceIndexHint hint = new MySqlForceIndexHint();
    // parseIndexHint(hint);
    // tableSource.getHints().add(hint);
  }

  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    lexer->nextToken();
    // 兼容jsqlparser 和presto
    if (lexer->token()->name->c_str() == Token::ON.name->c_str())
    {
      tableSource->setAlias(make_string_ptr("partition"));
    }
    else
    {
      accept(Token::LPAREN);
      this->exprParser->names((std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getPartitions(), tableSource);
      accept(Token::RPAREN);
    }
  }

  return tableSource;
}

SQLTableSource_ptr MySqlSelectParser::parseTableSourceRest(SQLTableSource_ptr tableSource)
{
  if (lexer->identifierEquals(FnvHash::Constants::TABLESAMPLE) && instanceof <SQLTableSource, SQLExprTableSource>(tableSource))
  {
    Lexer_SavePoint_ptr mark = lexer->mark();
    lexer->nextToken();

    SQLTableSampling_ptr sampling = SQLTableSampling_ptr(new SQLTableSampling());

    if (lexer->identifierEquals(FnvHash::Constants::BERNOULLI))
    {
      lexer->nextToken();
      sampling->setBernoulli(BOOL::TRUE);
    }
    else if (lexer->identifierEquals(FnvHash::Constants::SYSTEM))
    {
      lexer->nextToken();
      sampling->setSystem(BOOL::TRUE);
    }

    if (lexer->token()->name->c_str() == Token::LPAREN.name->c_str())
    {
      lexer->nextToken();

      if (lexer->identifierEquals(FnvHash::Constants::BUCKET))
      {
        lexer->nextToken();
        SQLExpr_ptr bucket = this->exprParser->primary();
        sampling->setBucket(bucket);

        if (lexer->token()->name->c_str() == Token::OUT.name->c_str())
        {
          lexer->nextToken();
          accept(Token::OF);
          SQLExpr_ptr outOf = this->exprParser->primary();
          sampling->setOutOf(outOf);
        }

        if (lexer->token()->name->c_str() == Token::ON.name->c_str())
        {
          lexer->nextToken();
          SQLExpr_ptr on = this->exprParser->getExpr();
          sampling->setOn(on);
        }
      }

      if (lexer->token()->name->c_str() == Token::LITERAL_INT.name->c_str() || lexer->token()->name->c_str() == Token::LITERAL_FLOAT.name->c_str())
      {
        SQLExpr_ptr val = this->exprParser->primary();

        if (lexer->identifierEquals(FnvHash::Constants::ROWS))
        {
          lexer->nextToken();
          sampling->setRows(val);
        }
        else if (lexer->token()->name->c_str() == Token::RPAREN.name->c_str())
        {
          sampling->setRows(val);
        }
        else
        {
          acceptIdentifier("PERCENT");
          sampling->setPercent(val);
        }
      }

      if (lexer->token()->name->c_str() == Token::IDENTIFIER.name->c_str())
      {
        string_ptr strVal = make_string_ptr(encode_util::UnicodeToUTF8(*lexer->stringVal()));
        char first = strVal->at(0);
        char last = strVal->at(strVal->length() - 1);
        if (last >= 'a' && last <= 'z')
        {
          last -= 32; // to upper
        }

        BOOL_ptr match = BOOL::FALSE;
        if ((first == '.' || (first >= '0' && first <= '9')))
        {
          switch (last)
          {
          case 'B':
          case 'K':
          case 'M':
          case 'G':
          case 'T':
          case 'P':
            match = BOOL::TRUE;
            break;
          default:
            break;
          }
        }
        // SQLSizeExpr* size = new SQLSizeExpr(strVal.substr(0, strVal.length() - 2), last);
        // sampling->setByteLength(size);
        lexer->nextToken();
      }

      SQLExprTableSource_ptr table = std::dynamic_pointer_cast<SQLExprTableSource>(tableSource);
      table->setSampling(sampling);

      accept(Token::RPAREN);
    }
    else
    {
      lexer->reset(mark);
    }
  }

  if (lexer->identifierEquals(FnvHash::Constants::USING))
  {
    return tableSource;
  }

  parseIndexHintList(tableSource);

  if (lexer->token()->name->c_str() == Token::PARTITION.name->c_str())
  {
    lexer->nextToken();
    accept(Token::LPAREN);
    this->exprParser->names((std::dynamic_pointer_cast<SQLExprTableSource>(tableSource))->getPartitions(), tableSource);
    accept(Token::RPAREN);
  }

  return SQLSelectParser::parseTableSourceRest(tableSource);
}

void MySqlSelectParser::parseIndexHintList(SQLTableSource_ptr tableSource)
{
  if (lexer->token()->name->c_str() == Token::USE.name->c_str())
  {
    lexer->nextToken();
    // MySqlUseIndexHint* hint = new MySqlUseIndexHint();
    // parseIndexHint(hint);
    // tableSource->getHints().add(hint);
    parseIndexHintList(tableSource);
  }

  if (lexer->identifierEquals(FnvHash::Constants::IGNORE))
  {
    lexer->nextToken();
    // MySqlIgnoreIndexHint* hint = new MySqlIgnoreIndexHint();
    // parseIndexHint(hint);
    // tableSource.getHints().add(hint);
    parseIndexHintList(tableSource);
  }

  if (lexer->identifierEquals(FnvHash::Constants::FORCE))
  {
    lexer->nextToken();
    // MySqlForceIndexHint hint = new MySqlForceIndexHint();
    // parseIndexHint(hint);
    // tableSource.getHints().add(hint);
    parseIndexHintList(tableSource);
  }
}

// void MySqlSelectParser::parseIndexHint(MySqlIndexHintImpl *hint)
// {
//   if (lexer->token()->name->c_str() == Token::INDEX)
//   {
//     lexer->nextToken();
//   }
//   else
//   {
//     accept(Token::KEY);
//   }

//   if (lexer->token()->name->c_str() == Token::FOR)
//   {
//     lexer->nextToken();

//     if (lexer->token()->name->c_str() == Token::JOIN)
//     {
//       lexer->nextToken();
//       // hint->setOption(MySqlIndexHint.Option.JOIN);
//     }
//     else if (lexer->token()->name->c_str() == Token::ORDER)
//     {
//       lexer->nextToken();
//       accept(Token::BY);
//       // hint.setOption(MySqlIndexHint.Option.ORDER_BY);
//     }
//     else
//     {
//       accept(Token::GROUP);
//       accept(Token::BY);
//       // hint.setOption(MySqlIndexHint.Option.GROUP_BY);
//     }
//   }

//   accept(Token::LPAREN);
//   while (lexer->token()->name->c_str() != Token::RPAREN && lexer->token()->name->c_str() != Token::XEOF)
//   {
//     if (lexer->token()->name->c_str() == Token::PRIMARY)
//     {
//       lexer->nextToken();
//       // hint.getIndexList().add(new SQLIdentifierExpr("PRIMARY"));
//     }
//     else
//     {
//       SQLName* name = this->exprParser->name();
//       name->setParent(hint);
//       hint->getIndexList().push_back(name);
//     }
//     if (lexer->token()->name->c_str() == Token::COMMA)
//     {
//       lexer->nextToken();
//     }
//     else
//     {
//       break;
//     }
//   }
//   accept(Token::RPAREN);
// }

std::shared_ptr<SQLUnionQuery> MySqlSelectParser::unionRest(std::shared_ptr<SQLUnionQuery> union_)
{
  if (lexer->token()->name->c_str() == Token::LIMIT.name->c_str())
  {
    union_->setLimit(this->exprParser->parseLimit());
  }
  return SQLSelectParser::unionRest(union_);
}

// MySqlExprParser *MySqlSelectParser::getExprParser()
// {
//   return (MySqlExprParser *)exprParser;
// }
